"""Independent MCP server for Dayu (大鱼号).

This leaves the original Xiaohongshu server untouched. Start via:

    python -m xhs_mcp_server.dayu_server

Tools provided (async job style like xhs server):
 - create_dayu_article / _async / _wait
 - get_job_status (shared pattern)
 - ping

Environment Variables (override behaviour):
 - DAYU_HEADLESS          : run headless Chrome (true/false)
 - DAYU_USER_DATA_DIR     : path to Chrome user data for login persistence
 - DAYU_PUBLISH_URL       : compose article page url
 - DAYU_WAIT_TIMEOUT      : explicit wait seconds
 - DAYU_KEEP_BROWSER_OPEN : keep browser after submit (true/false)
 - DAYU_SLOW_MODE         : extra small sleeps (true/false)
 - DAYU_SYNC_WAIT_SECONDS : default wait for *wait tools (default 150)

"""
from __future__ import annotations

import threading
import uuid
import traceback
import time
import os
from typing import List

from mcp.server import FastMCP
from mcp.types import TextContent

from .dayu import DayuPoster

mcp = FastMCP("dayu")
SYNC_WAIT_SECONDS = int(os.getenv("DAYU_SYNC_WAIT_SECONDS", "150"))

# ---------------- Job Infrastructure (in-memory) ----------------
JOBS: dict[str, dict] = {}

def _set_job(job_id: str, **kwargs):
    job = JOBS.get(job_id, {})
    job.update(kwargs)
    JOBS[job_id] = job

def _start_job(target, *args, **kwargs) -> str:
    job_id = str(uuid.uuid4())
    _set_job(job_id, status="pending", result=None, error=None)

    def runner():
        _set_job(job_id, status="running")
        try:
            res = target(*args, **kwargs)
            _set_job(job_id, status="done", result=res, error=None)
        except Exception:
            _set_job(job_id, status="error", error=traceback.format_exc(), result=None)
    th = threading.Thread(target=runner, daemon=True)
    th.start()
    return job_id

def _wait_for_job(job_id: str, timeout_sec: int) -> tuple[bool, str]:
    start = time.time()
    while time.time() - start < timeout_sec:
        job = JOBS.get(job_id)
        if job:
            status = job.get("status")
            if status == "done":
                return True, str(job.get("result"))
            if status == "error":
                return True, f"error: {job.get('error')}"
        time.sleep(1)
    return False, f"timeout waiting job {job_id}"

# ---------------- Tools ----------------
@mcp.tool()
def ping(message: str = "ping") -> list[TextContent]:
    return [TextContent(type="text", text=f"pong: {message}")]

@mcp.tool()
def login_dayu(wait_seconds: int | None = None, account: str | None = None) -> list[TextContent]:
    """Open Dayu dashboard and wait (manual login). Returns status when avatar/logout detected.

    Args:
        wait_seconds: override max wait (default from DAYU_LOGIN_MAX_WAIT or 180)
        account: account name for multi-account Chrome profiles
    """
    def job_func():
        poster = DayuPoster(account=account)
        try:
            poster.publish_article(title="", content="", images=None)  # triggers open + ensure_logged_in; will not actually submit
            # we only care about login; do not proceed to fill if empty
            return {"logged_in": poster._is_logged_in(), "note": "login check done"}
        finally:
            if not poster.config.keep_browser_open:
                poster.close()
    job_id = _start_job(job_func)
    return [TextContent(type="text", text=f"job_id={job_id}")]


def _normalize_media(images_or_list) -> list[str]:
    if images_or_list is None:
        return []
    if isinstance(images_or_list, str):
        return [images_or_list]
    if isinstance(images_or_list, (list, tuple)):
        return [str(x) for x in images_or_list]
    raise TypeError("images parameter must be str | list[str] | tuple")


@mcp.tool()
def create_dayu_article(
    title: str,
    content: str,
    images: list | str | None = None,
    author_name: str | None = None,
    cover_single: bool | None = None,
    cover_from_content: bool | None = None,
    cover_vertical: bool | None = None,
    info_source: str | None = None,
    account: str | None = None,
) -> list[TextContent]:
    """Start creating a Dayu article asynchronously (returns job_id).

    Optional args mirror poster.publish_article for finer control without env vars.
    New: account parameter for multi-account Chrome profiles.
    """
    imgs = _normalize_media(images)

    def job_func():
        poster = DayuPoster(account=account)
        try:
            result = poster.publish_article(
                title=title,
                content=content,
                images=imgs,
                author_name=author_name,
                cover_single=cover_single,
                cover_from_content=cover_from_content,
                cover_vertical=cover_vertical,
                info_source=info_source,
            )
            return result
        finally:
            # poster decides closing based on keep_browser_open
            if poster.config.keep_browser_open:
                pass

    job_id = _start_job(job_func)
    return [TextContent(type="text", text=f"job_id={job_id}")]


@mcp.tool()
def create_dayu_article_async(
    title: str,
    content: str,
    images: list | str | None = None,
    author_name: str | None = None,
    cover_single: bool | None = None,
    cover_from_content: bool | None = None,
    cover_vertical: bool | None = None,
    info_source: str | None = None,
    account: str | None = None,
) -> list[TextContent]:
    return create_dayu_article(
        title=title,
        content=content,
        images=images,
        author_name=author_name,
        cover_single=cover_single,
        cover_from_content=cover_from_content,
        cover_vertical=cover_vertical,
        info_source=info_source,
        account=account,
    )


@mcp.tool()
def create_dayu_article_wait(
    title: str,
    content: str,
    images: list | str | None = None,
    wait_seconds: int | None = None,
    author_name: str | None = None,
    cover_single: bool | None = None,
    cover_from_content: bool | None = None,
    cover_vertical: bool | None = None,
    info_source: str | None = None,
    account: str | None = None,
) -> list[TextContent]:
    resp = create_dayu_article(
        title=title,
        content=content,
        images=images,
        author_name=author_name,
        cover_single=cover_single,
        cover_from_content=cover_from_content,
        cover_vertical=cover_vertical,
        info_source=info_source,
        account=account,
    )
    job_id = resp[0].text.split("job_id=")[-1]
    timeout = wait_seconds if isinstance(wait_seconds, int) and wait_seconds > 0 else SYNC_WAIT_SECONDS
    finished, msg = _wait_for_job(job_id, timeout)
    if finished:
        return [TextContent(type="text", text=msg)]
    return [TextContent(type="text", text=f"waiting: {msg}; job_id={job_id}")]


@mcp.tool()
def get_job_status(job_id: str) -> list[TextContent]:
    job = JOBS.get(job_id)
    if not job:
        return [TextContent(type="text", text=f"not_found: {job_id}")]
    status = job.get("status")
    if status == "done":
        return [TextContent(type="text", text=f"done: {job.get('result')}")]
    if status == "error":
        return [TextContent(type="text", text=f"error: {job.get('error')}")]
    return [TextContent(type="text", text=status)]


def main():  # entry point
    mcp.run()


if __name__ == "__main__":  # pragma: no cover
    main()
